package cn.classg.redisson.conf;

import cn.classg.redisson.aspect.LockAspectHandler;
import cn.classg.redisson.aspect.LockInfoProvider;
import cn.classg.redisson.enums.ServerPattern;
import cn.classg.redisson.factory.LockServiceFactory;
import cn.classg.redisson.factory.ServerConfigInitFactory;
import cn.classg.redisson.factory.ServerPatternFactory;
import cn.classg.redisson.properties.LockConfigProperties;
import cn.classg.redisson.service.impl.*;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import javax.annotation.Resource;

/**
 * redis分布式锁自动配置
 * 必须在Redis自动配置之后开始配置
 * @author CLASSG
 */
@Configuration
@Import({LockAspectHandler.class})
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableConfigurationProperties(LockConfigProperties.class)
public class RedisLockAutoConfiguration {

    /**
     * 注入Redis分布式锁的配置属性
     */
    @Resource
    private LockConfigProperties lockConfig;

    /**
     * 按不同的配置决定注入的RedissonClient，后面加锁需要基于RedissonClient去封装
     * 属性参考：https://www.bookstack.cn/read/redisson-wiki-zh/spilt.8.2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95.md
     *
     * @return org.redisson.api.RedissonClient
     * @author CLASSG
     */
    @Bean(name = "lockRedissonClient", destroyMethod = "shutdown")
    @ConditionalOnMissingBean
    RedissonClient redisson() throws Exception {
        // ====================================不同模式的公有配置====================================
        Config config = new Config();
        // RTopic共享线程数量
        config.setThreads(lockConfig.getThreads());
        // Redisson使用的所有redis客户端之间共享的线程数量
        config.setNettyThreads(lockConfig.getNettyThreads());
        // 仅在没有leaseTimeout参数定义的情况下获取锁定时才使用此参数。看门狗过期时间
        config.setLockWatchdogTimeout(lockConfig.getLockWatchdogTimeout());
        // 是否串行处理消息
        config.setKeepPubSubOrder(lockConfig.getKeepPubSubOrder());
        // 否在Redis端使用Lua脚本缓存
        config.setUseScriptCache(lockConfig.getUseScriptCache());
        // 根据用户在配置文件中配置的pattern模式获取分布式锁的服务模式（单机、集群、哨兵、主从）
        ServerPattern serverPattern = ServerPatternFactory.getServerPattern(lockConfig.getPattern());

        // ====================================不同模式的特有配置====================================
        // 为了创建不同的连接池（MasterConnectionPool、MasterPubSubConnectionPool、SlaveConnectionPool和PubSubConnectionPool）
        // 参考：https://blog.csdn.net/zilong_zilong/article/details/78609423

        // 适配不同的Redis部署模式
        switch (serverPattern) {
            // 单机模式
            case SINGLE:
                // 指定使用单节点部署方式，通过config获取单机模式server配置
                SingleServerConfig singleServerConfig = config.useSingleServer();
                ServerConfigInitFactory.initSingleConfig(singleServerConfig, lockConfig);
                break;
            // 主从模式
            case MASTER_SLAVE:
                MasterSlaveServersConfig masterSlaveServersConfig = config.useMasterSlaveServers();
                ServerConfigInitFactory.initMasterSlaveConfig(masterSlaveServersConfig, lockConfig);
                break;
            // 集群模式
            case CLUSTER:
                ClusterServersConfig clusterServersConfig = config.useClusterServers();
                ServerConfigInitFactory.initClusterConfig(clusterServersConfig, lockConfig);
                break;
            // 云托管模式
            case REPLICATED:
                ReplicatedServersConfig replicatedServersConfig = config.useReplicatedServers();
                ServerConfigInitFactory.initReplicatedServersConfig(replicatedServersConfig, lockConfig);
                break;
            // 哨兵模式
            case SENTINEL:
                SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
                ServerConfigInitFactory.initSentinelServersConfig(sentinelServersConfig, lockConfig);
                break;
            default:
                break;
        }
        // 根据自己定义的配置创建一个RedissonClient实例并注入容器（这一步非常耗时，预计1~2秒）
        return Redisson.create(config);
    }

    @Bean
    public LockInfoProvider lockInfoProvider() {
        return new LockInfoProvider();
    }

    @Bean
    public LockServiceFactory lockFactory() {
        return new LockServiceFactory();
    }

    @Bean
//    @Scope("prototype")
    public FairLockServiceImpl fairLockService() {
        return new FairLockServiceImpl();
    }

    @Bean
//    @Scope("prototype")
    public ReentrantLockServiceImpl reentrantLockService() {
        return new ReentrantLockServiceImpl();
    }

    @Bean
//    @Scope("prototype")
    public ReadLockServiceImpl readLockService() {
        return new ReadLockServiceImpl();
    }

    @Bean
//    @Scope("prototype")
    public WriteLockServiceImpl writeLockService() {
        return new WriteLockServiceImpl();
    }

    @Bean
//    @Scope("prototype")
    public MultiLockServiceImpl multiLockService() {
        return new MultiLockServiceImpl();
    }

    @Bean
//    @Scope("prototype")
    public RedLockServiceImpl redLockService() {
        return new RedLockServiceImpl();
    }

}
